//
// Created by wxy on 24-12-15.
//
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/jiffies.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/timer.h>
#include <linux/semaphore.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>

#define TIMER_CNT 1
#define TIMER_NAME "timer"

#define CLOSE_CMD _IO(0xEF, 1)     // close the timer
#define OPEN_CMD _IO(0xEF, 2)      // Open the timer
#define SETPERIOD_CMD _IO(0xEF, 3) // Set the period of the timer;

struct timer_dev
{
    dev_t devid;           // 设备号
    struct cdev cdev;      // 字符设备结构
    struct class *class;   // 类
    struct device *device; // 设备
    int major;             //
    int minor;
    struct device_node *node; // 设备树节点
    struct timer_list timer;  // 定时器
    int timerperiod;          // 定时器周期
    int led_gpio;             // led的gpio编号
};
static struct timer_dev timerdev;

static void timer_handler(unsigned long arg)
{

    struct timer_dev *dev = (struct timer_dev *)arg;
    // printk("timer_handler\r\n");
    static int sta = 0;
    sta = !sta;
    gpio_set_value(dev->led_gpio, sta);
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timerperiod));
}
static int tiemr_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &timerdev;
    return 0;
}
static int timer_release(struct inode *inode, struct file *filp)
{
    return 0;
}
static long timer_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int value = 0;
    int ret = 0;
    struct timer_dev *dev = (struct timer_dev *)filp->private_data;
    printk("timer _ioctl\r\n");
    switch (cmd)
    {
    case CLOSE_CMD:
        del_timer_sync(&dev->timer);
        break;
    case OPEN_CMD:
        mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timerperiod));
        break;
    case SETPERIOD_CMD:

        if (ret < 0)
        {
            return -EINVAL;
        }
        dev->timerperiod = arg;
        printk("arg is set = %d\r\n", arg);
        mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timerperiod));
        break;
    }
    return 0;
}

static struct file_operations timer_fops = {
    .owner = THIS_MODULE,
    .open = tiemr_open,
    .release = timer_release,
    .unlocked_ioctl = timer_ioctl,
};

static int led_init(struct timer_dev *dev)
{
    int ret = 0;
    dev->node = of_find_node_by_path("/gpioled");
    if (dev->node == NULL)
    {
        printk("node not find\n");
        ret = -EINVAL;
        goto fail_find;
    }
    dev->led_gpio = of_get_named_gpio(dev->node, "led-gpios", 0);
    if (dev->led_gpio < 0)
    {
        printk("get led-gpios failed!\r\n");
        ret = -EINVAL;
        goto fail_get_gpio;
    }
    ret = gpio_request(dev->led_gpio, "led");
    if (ret)
    {
        printk("gpio request failed!\r\n");
        ret = -EINVAL;
        goto fail_gpio_request;
    }
    ret = gpio_direction_output(dev->led_gpio, 1);
    if (ret)
    {
        printk("gpio direction set failed!\r\n");
        ret = -EINVAL;
        goto fail_gpio_direction;
    }
    return 0;
fail_gpio_direction:
    gpio_free(dev->led_gpio);
fail_gpio_request:
fail_get_gpio:
fail_find:
    return ret;
}

static int __init timer_init(void)
{
    int ret = 0;
    printk("timer init\n");
    // 1、创建设备号
    timerdev.major = 0;
    if (timerdev.major)
    {
        timerdev.devid = MKDEV(timerdev.major, 0);
        ret = register_chrdev_region(timerdev.devid, TIMER_CNT, TIMER_NAME);
    }
    else
    {
        ret = alloc_chrdev_region(&timerdev.devid, 0, TIMER_CNT, TIMER_NAME);
    }
    if (ret < 0)
    {
        printk("register chrdev failed!\r\n");
        goto fail_devid;
    }
    printk("major=%d,minor=%d\r\n", MAJOR(timerdev.devid), MINOR(timerdev.devid));
    // 2、创建字符设备
    timerdev.cdev.owner = THIS_MODULE;
    cdev_init(&timerdev.cdev, &timer_fops);
    ret = cdev_add(&timerdev.cdev, timerdev.devid, TIMER_CNT);
    if (ret < 0)
    {
        printk("add cdev failed!\r\n");
        goto fail_cdevadd;
    }
    // 3、创建类
    timerdev.class = class_create(THIS_MODULE, TIMER_NAME);
    if (IS_ERR(timerdev.class))
    {
        printk("create class failed!\r\n");
        ;
        ret = PTR_ERR(timerdev.class);
        goto fail_class;
    }
    // 4、创建设备
    timerdev.device = device_create(timerdev.class, NULL, timerdev.devid, NULL, TIMER_NAME);
    if (IS_ERR(timerdev.device))
    {
        printk("create device failed!\r\n");
        ret = PTR_ERR(timerdev.device);
        goto fail_device;
    }
    //  5、注册字符设备驱动程序 初始化led
    ret = led_init(&timerdev);
    if (ret)
    {
        printk("led_init failed!\r\n");
        goto fail_ledinit;
    }
    // 6、初始化定时器
    init_timer(&timerdev.timer);
    timerdev.timerperiod = 500;
    timerdev.timer.function = timer_handler;
    timerdev.timer.expires = jiffies + msecs_to_jiffies(1000); // 定时器超时时间
    timerdev.timer.data = (unsigned long)&timerdev;            // 定时器参数,多个定时器时区分是哪个定时器超时
    add_timer(&timerdev.timer);
    return 0;
fail_ledinit:
    device_destroy(timerdev.class, timerdev.devid);
fail_device:
    class_destroy(timerdev.class);
fail_class:
    cdev_del(&timerdev.cdev);
fail_cdevadd:
    unregister_chrdev_region(timerdev.devid, TIMER_CNT);
fail_devid:
    return ret;
}
static void __exit timer_exit(void)
{
    gpio_set_value(timerdev.led_gpio, 1);
    del_timer(&timerdev.timer);
    cdev_del(&timerdev.cdev);
    unregister_chrdev_region(timerdev.devid, TIMER_CNT);
    device_destroy(timerdev.class, timerdev.devid);
    class_destroy(timerdev.class);
    gpio_free(timerdev.led_gpio);
    // nothing to do
}
module_init(timer_init);
module_exit(timer_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("WXY");
MODULE_DESCRIPTION("timer");
MODULE_ALIAS("timer");
MODULE_VERSION("V1.0");